// 
// Camera.cs
//  
// Author:
//       Tomas Vondracek <tomas.vondracek@gmail.com>
// 
// Copyright (c) 2009 Tomas Vondracek
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;

using OpenTK.Graphics;
using OpenTK;
//using OpenTK.Math;

namespace Game
{
	public enum CameraMove
	{
		Forward,
		Back,
		Left,
		Right
	}
	
	public class Camera
	{
		OpenTK.Vector3 position;
		Vector3 lookAt;
		Vector3 direction;
		
		bool invertView;
		
		float speed;
		float sensitivity;
		
		float timeStep;

		public Vector3 LookAt {
			get {
				return lookAt;
			}
		}

		public Vector3 Position {
			get {
				return position;
			}
		}

		public float Sensitivity {
			get {
				return sensitivity;
			}
			set {
				sensitivity = value;
			}
		}

		public float Speed {
			get {
				return speed;
			}
			set {
				speed = value;
			}
		}

		public bool Invert {
			get {
				return invertView;
			}
			set {
				invertView = value;
			}
		}		
		
		public Camera()
		{
			this.position = new Vector3(0,0,0);
			this.lookAt = new Vector3(0,0,1);
			
			this.direction = this.lookAt - this.position;
			
			this.speed = 200f;
			this.sensitivity = 2f;
			this.timeStep = 0f;
			
			this.invertView = false;
		}
		
		public void UpdateCamera(double timeStep)
		{
			this.timeStep = (float)timeStep;
			
			GL.LoadIdentity ();
			Glu.LookAt (this.position, this.lookAt, Vector3.UnitY);
		}
		
		public void Move(CameraMove move)
		{			
			float angle = 0f;
			
			switch (move) 
			{
				case CameraMove.Back: 
					angle = (float)Math.PI;
					break;
				case CameraMove.Forward:
					angle = 0f;
					break;
				case CameraMove.Left:
					angle = 0.5f * (float)Math.PI;
					break;
				case CameraMove.Right:
					angle = -0.5f * (float) Math.PI;
					break;
			}
			
			MoveByAngle (angle);
		}

		public void MoveByAngle (float angle)
		{
			Vector3 dir = this.lookAt - this.position;
			dir.Normalize ();
			dir = Vector3.Transform (dir, Matrix4.RotateY (angle)).Xyz;
			
			this.position += ((this.timeStep * this.speed) * dir);
			this.lookAt += ((this.timeStep * this.speed) * dir);
		}
		
		/// <summary>
        /// look by mouse move
        /// </summary>
        /// <param name="x">Vychyleni v horizontalnim smeru</param>
        /// <param name="y">Vychyleni ve vertikalnim smeru</param>
        public void LookAround(float x, float y)
        {
            Vector3 dir = this.lookAt - this.position;

            dir.Normalize();

            Vector3 ndir = Vector3.Cross(dir, Vector3.UnitY);
            ndir.Normalize();

            Vector3 undir = Vector3.Cross(dir, ndir);
            undir.Normalize();

            float invert = 1;

            if (this.invertView)
                invert *= -1;

            Vector3 dx = (this.sensitivity / 2000f) * x * ndir;
            Vector3 dy = invert * (this.sensitivity / 2000f) * y * undir;

            Vector3 newLookAt = this.lookAt;

            newLookAt = newLookAt + dx;
            newLookAt = newLookAt + dy;

            Vector3 newdir = newLookAt - position;
            newdir.Normalize();


            if (newdir.Y > -0.9999f && newdir.Y < 0.9999f)
            	this.lookAt = this.position + newdir;
        }

	}
}
